package com.hzqc.wxaapp.sdk;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.Objects;

/**
 * @author n1
 * @since 2021/6/18 10:05
 */

public class WechatAppTokenService {
    private final RestTemplateBuilder restTemplateBuilder;
    private final MiniAppTokenStorage miniAppTokenStorage;
    private final MiniAppClientService miniAppClientService;
    private final RestTemplate restTemplate;


    public WechatAppTokenService(RestTemplateBuilder restTemplateBuilder,
                                 MiniAppTokenStorage miniAppTokenStorage,
                                 MiniAppClientService miniAppClientService) {
        restTemplateBuilder.errorHandler(new LogResponseErrorHandler());
        this.restTemplateBuilder = restTemplateBuilder;
        this.restTemplate = restTemplateBuilder.build();
        this.restTemplate.getMessageConverters().add(0, messageConverter());
        this.miniAppTokenStorage = miniAppTokenStorage;
        this.miniAppClientService = miniAppClientService;
    }


    public synchronized String getToken(String clientId) {
        MiniAppClient appClient = miniAppClientService.get(clientId);
        String cachedToken = miniAppTokenStorage.get(appClient.getAppId());
        if (StringUtils.hasText(cachedToken)) {
            return cachedToken;
        }
        return tokenRequest(appClient);
    }

    public RestTemplate getRestTemplate() {
        RestTemplate restTemplate = restTemplateBuilder.build();
        restTemplate.getMessageConverters().add(0, messageConverter());
        restTemplate.getMessageConverters().add(4, new MiniByteArrayResourceHttpMessageConverter());
        return restTemplate;
    }

    private MappingJackson2HttpMessageConverter messageConverter() {

        SimpleFilterProvider provider = new SimpleFilterProvider();
        provider.addFilter("ig_base", SimpleBeanPropertyFilter.serializeAllExcept("client_id", "download"));
        provider.addFilter("ig_clientId", SimpleBeanPropertyFilter.serializeAllExcept("client_id"));
        provider.addFilter("ig_path", SimpleBeanPropertyFilter.serializeAllExcept("path", "client_id", "download"));

        ObjectMapper objectMapper = new ObjectMapper()
                .setFilterProvider(provider)
                .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // empty string error
                .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .registerModule(new JavaTimeModule());
        return new MappingJackson2HttpMessageConverter(objectMapper);
    }

    private String tokenRequest(MiniAppClient appClient) {
        String appId = appClient.getAppId();
        URI uri = UriComponentsBuilder.fromHttpUrl("https://api.weixin.qq.com/cgi-bin/token")
                .queryParam("grant_type", "client_credential")
                .queryParam("appid", appId)
                .queryParam("secret", appClient.getSecret())
                .build()
                .toUri();
        WechatAccessToken object = restTemplate.getForObject(uri, WechatAccessToken.class);

        if (Objects.isNull(object) || !StringUtils.hasText(object.getAccessToken())) {
            throw new RuntimeException("小程序服务异常：" + object.getErrmsg() + "，请重试，");
        }
        String accessToken = object.getAccessToken();
        miniAppTokenStorage.put(appId, accessToken);
        return accessToken;
    }
}
